热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

嫌疑|多种_爬虫反爬:JS逆向实战练习1

篇首语:本文由编程笔记#小编为大家整理,主要介绍了爬虫反爬:JS逆向实战练习1相关的知识,希望对你有一定的参考价值。1.简介快速优雅地学会J

篇首语:本文由编程笔记#小编为大家整理,主要介绍了爬虫反爬:JS逆向实战练习1相关的知识,希望对你有一定的参考价值。



1. 简介

快速优雅地学会JS逆向,就需要从实战开始,接下来我会提供Base64加密的原网址以及接口参数,从实战中学习如何下断点、抠代码、本地运行等操作,此技术一般用于爬虫上,是一个爬虫程序猿进阶的必经之路。


2. 实战信息

网址:
aHR0cHM6Ly9mYW55aS5iYWlkdS5jb20vP2FsZHR5cGU9MTYwNDcjYXV0by96aA==
接口:
aHR0cHM6Ly9mYW55aS5iYWlkdS5jb20vdjJ0cmFuc2FwaT9mcm9tPXpoJnRvPWVu
逆向参数:


  • sign:232427.485594
  • token:3dde9ef10b6f6ae310af38e6f1bd564f

3. 实战流程


3.1抓包找接口

首先,进入页面按F12,打开控制面板,调到Network板块后在翻译处写入需要翻译的信息(我这里输入的是“你好”)触发网络请求,打开请求面板查看该请求的具体信息。


3.1.1 General



请求信息



  • URL:请求API地址
  • Method:请求方式GET/POST

3.1.2 Request headers



除了自定义请求头参数,其余Host、Origin、Referer一般为爬虫必须参数,COOKIE看站点是否有对其校验,如果有特殊的自定义参数一般也为爬虫需要。



  • Acs-Token:自定义的校验参数
  • COOKIE:客户端缓存信息
  • Host:域名
  • Origin:来源信息
  • Referer:防止跨站攻击

3.2 查找加密参数


从上图中可以看到sign和token有加密嫌疑,所以目标就暂定为对这两个参数进行一个简单逆向。


3.3 断点分类

下断多种方法:


  • 元素下断
  • 事件下断
  • XHR下断
  • 搜索下断

3.4 学会快速下断

这里主要展示XHR下断和搜索下断,在此比较好用。
XHR断点:首先复制请求链接?前的部分路径,这里是/v2transapi,到Source下的XHR断点处下断,回车输入新翻译内容即可触发。


可以看到断点断在了send()方法的调用处,在Call stack中查看函数方法的调用栈,目前代码可读性太低,我们需要对其进行格式化查看,就需要左下角的此按钮。

在调用栈中查看自己所需参数的作用域,首先在调用栈中找到加密后的参数位置,然后往前推,直到找到加密的方法,此处直接展示寻找结果。

如上图,作用域的位置就看提示的参数数据即可,windows下如果使用Chrome可以用ctrl键触发,这里查看的是ajax()的方法栈,同时我们可以找到加密后的结果,鼠标放到函数参数里可以看到,我们也可以直接从Scope中查看然后返回源码找。

此处左右两侧都可以看到一个加密后的sign和token数据,就往前找调用的方法栈即可。

再往前一个方法栈就可以看到,headers里面必须有个"Acs-Token"的参数,data中的数据来源是b,往上看b是一个对象,请求内容在对象里,sign:x(n)|token:window.common.token这两个参数是这样获取的。
我们可以直接知道x(n)就是sign的加密函数,token存在window.common下。

鼠标放置在x函数上会给一个跳转提示,点击index_61616b2.js有利于我们直接找到函数调用的方法,我们可以直接去抠代码本地运行,n鼠标放上去可以发现就是"你好",将断点打在8782行,重新键入更改断点作用域。

测试确认x(n)方法确实是sign获取的方法位置,进入函数。

此处可以直接抠到本地用Node运行,代码调用可以看到调用了两个作用域里的函数,所以对代码抠全,进行部分改写,此处直接粘代码。

function a(r)
if (Array.isArray(r))
for (var o &#61; 0, t &#61; Array(r.length); o < r.length; o&#43;&#43;)
t[o] &#61; r[o];
return t

return Array.from(r)

function n(r, o)
for (var t &#61; 0; t < o.length - 2; t &#43;&#61; 3)
var a &#61; o.charAt(t &#43; 2);
a &#61; a >&#61; "a" ? a.charCodeAt(0) - 87 : Number(a),
a &#61; "&#43;" &#61;&#61;&#61; o.charAt(t &#43; 1) ? r >>> a : r << a,
r &#61; "&#43;" &#61;&#61;&#61; o.charAt(t) ? r &#43; a & 4294967295 : r ^ a

return r

function e(r)
var o &#61; r.match(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g);
if (null &#61;&#61;&#61; o)
var t &#61; r.length;
t > 30 && (r &#61; "" &#43; r.substr(0, 10) &#43; r.substr(Math.floor(t / 2) - 5, 10) &#43; r.substr(-10, 10))
else
for (var e &#61; r.split(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/), C &#61; 0, h &#61; e.length, f &#61; []; h > C; C&#43;&#43;)
"" !&#61;&#61; e[C] && f.push.apply(f, a(e[C].split(""))),
C !&#61;&#61; h - 1 && f.push(o[C]);
var g &#61; f.length;
g > 30 && (r &#61; f.slice(0, 10).join("") &#43; f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) &#43; 5).join("") &#43; f.slice(-10).join(""))

var u &#61; void 0
, l &#61; "" &#43; String.fromCharCode(103) &#43; String.fromCharCode(116) &#43; String.fromCharCode(107);
u &#61; null !&#61;&#61; i ? i : (i &#61; window[l] || "") || "";
for (var d &#61; u.split("."), m &#61; Number(d[0]) || 0, s &#61; Number(d[1]) || 0, S &#61; [], c &#61; 0, v &#61; 0; v < r.length; v&#43;&#43;)
var A &#61; r.charCodeAt(v);
128 > A ? S[c&#43;&#43;] &#61; A : (2048 > A ? S[c&#43;&#43;] &#61; A >> 6 | 192 : (55296 &#61;&#61;&#61; (64512 & A) && v &#43; 1 < r.length && 56320 &#61;&#61;&#61; (64512 & r.charCodeAt(v &#43; 1)) ? (A &#61; 65536 &#43; ((1023 & A) << 10) &#43; (1023 & r.charCodeAt(&#43;&#43;v)),
S[c&#43;&#43;] &#61; A >> 18 | 240,
S[c&#43;&#43;] &#61; A >> 12 & 63 | 128) : S[c&#43;&#43;] &#61; A >> 12 | 224,
S[c&#43;&#43;] &#61; A >> 6 & 63 | 128),
S[c&#43;&#43;] &#61; 63 & A | 128)

for (var p &#61; m, F &#61; "" &#43; String.fromCharCode(43) &#43; String.fromCharCode(45) &#43; String.fromCharCode(97) &#43; ("" &#43; String.fromCharCode(94) &#43; String.fromCharCode(43) &#43; String.fromCharCode(54)), D &#61; "" &#43; String.fromCharCode(43) &#43; String.fromCharCode(45) &#43; String.fromCharCode(51) &#43; ("" &#43; String.fromCharCode(94) &#43; String.fromCharCode(43) &#43; String.fromCharCode(98)) &#43; ("" &#43; String.fromCharCode(43) &#43; String.fromCharCode(45) &#43; String.fromCharCode(102)), b &#61; 0; b < S.length; b&#43;&#43;)
p &#43;&#61; S[b],
p &#61; n(p, F);
return p &#61; n(p, D),
p ^&#61; s,
0 > p && (p &#61; (2147483647 & p) &#43; 2147483648),
p %&#61; 1e6,
p.toString() &#43; "." &#43; (p ^ m)

console.log(e(&#39;你好&#39;))

调试发现报错(命令行输入node xx.js)&#xff1a;

因为本地环境没有window对象&#xff0c;因为l是一步计算的到为固定值&#xff0c;我们需要获取到window[l]的值&#xff0c;就先获取l的值&#xff0c;鼠标放在l上即可获取因为此算法为固定值&#xff0c;获取到l &#61; “gtk”&#xff0c;所以此处的window[l] &#61;&#61;&#61; window[“gtk”],我们通过搜索ctrl&#43;shift&#43;f/Command&#43;shift&#43;f调出搜索面板&#xff0c;通过搜索(window[“gtk”]&#xff5c;window[‘gtk’]&#xff5c;window.gtk)这三个方法去查找&#xff0c;就看此处调用哪个&#xff0c;某度翻译用的window.gtk&#xff0c;是一个固定值。

直接抠值替换源码里的window[l]即可&#xff0c;调试发现i还是undefined,所以在变量上定义一个i的初始化方法即可。

再次测试调用&#xff0c;对比浏览器请求参数。


结果一致就说明获取成功了&#xff0c;获取到sign了还有个token值没有获取&#xff0c;这个又怎么获取呢。上面我们发现是window.common.token产生&#xff0c;通过搜索(window[“common”]&#xff5c;window[‘common’]&#xff5c;window.common)先找父节点值&#xff0c;这里用window[‘common’]即可搜到&#xff0c;发现common是页面定义的变量&#xff0c;刷新页面发现token值写死我们这里就直接获取到了token值。


4. JS完整代码

var token &#61; "3dde9ef10b6f6ae310af38e6f1bd564f"
function a(r)
if (Array.isArray(r))
for (var o &#61; 0, t &#61; Array(r.length); o < r.length; o&#43;&#43;)
t[o] &#61; r[o];
return t

return Array.from(r)

function n(r, o)
for (var t &#61; 0; t < o.length - 2; t &#43;&#61; 3)
var a &#61; o.charAt(t &#43; 2);
a &#61; a >&#61; "a" ? a.charCodeAt(0) - 87 : Number(a),
a &#61; "&#43;" &#61;&#61;&#61; o.charAt(t &#43; 1) ? r >>> a : r << a,
r &#61; "&#43;" &#61;&#61;&#61; o.charAt(t) ? r &#43; a & 4294967295 : r ^ a

return r

function e(r)
var o &#61; r.match(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/g);
if (null &#61;&#61;&#61; o)
var t &#61; r.length;
t > 30 && (r &#61; "" &#43; r.substr(0, 10) &#43; r.substr(Math.floor(t / 2) - 5, 10) &#43; r.substr(-10, 10))
else
for (var e &#61; r.split(/[\\uD800-\\uDBFF][\\uDC00-\\uDFFF]/), C &#61; 0, h &#61; e.length, f &#61; []; h > C; C&#43;&#43;)
"" !&#61;&#61; e[C] && f.push.apply(f, a(e[C].split(""))),
C !&#61;&#61; h - 1 && f.push(o[C]);
var g &#61; f.length;
g > 30 && (r &#61; f.slice(0, 10).join("") &#43; f.slice(Math.floor(g / 2) - 5, Math.floor(g / 2) &#43; 5).join("") &#43; f.slice(-10).join(""))

var u &#61; void 0
, l &#61; "" &#43; String.fromCharCode(103) &#43; String.fromCharCode(116) &#43; String.fromCharCode(107);
var i &#61; null;
u &#61; null !&#61;&#61; i ? i : (i &#61; "320305.131321201" || "") || "";
for (var d &#61; u.split("."), m &#61; Number(d[0]) || 0, s &#61; Number(d[1]) || 0, S &#61; [], c &#61; 0, v &#61; 0; v < r.length; v&#43;&#43;)
var A &#61; r.charCodeAt(v);
128 > A ? S[c&#43;&#43;] &#61; A : (2048 > A ? S[c&#43;&#43;] &#61; A >> 6 | 192 : (55296 &#61;&#61;&#61; (64512 & A) && v &#43; 1 < r.length && 56320 &#61;&#61;&#61; (64512 & r.charCodeAt(v &#43; 1)) ? (A &#61; 65536 &#43; ((1023 & A) << 10) &#43; (1023 & r.charCodeAt(&#43;&#43;v)),
S[c&#43;&#43;] &#61; A >> 18 | 240,
S[c&#43;&#43;] &#61; A >> 12 & 63 | 128) : S[c&#43;&#43;] &#61; A >> 12 | 224,
S[c&#43;&#43;] &#61; A >> 6 & 63 | 128),
S[c&#43;&#43;] &#61; 63 & A | 128)

for (var p &#61; m, F &#61; "" &#43; String.fromCharCode(43) &#43; String.fromCharCode(45) &#43; String.fromCharCode(97) &#43; ("" &#43; String.fromCharCode(94) &#43; String.fromCharCode(43) &#43; String.fromCharCode(54)), D &#61; "" &#43; String.fromCharCode(43) &#43; String.fromCharCode(45) &#43; String.fromCharCode(51) &#43; ("" &#43; String.fromCharCode

推荐阅读
  • 在PHP中如何正确调用JavaScript变量及定义PHP变量的方法详解 ... [详细]
  • 本指南介绍了如何在ASP.NET Web应用程序中利用C#和JavaScript实现基于指纹识别的登录系统。通过集成指纹识别技术,用户无需输入传统的登录ID即可完成身份验证,从而提升用户体验和安全性。我们将详细探讨如何配置和部署这一功能,确保系统的稳定性和可靠性。 ... [详细]
  • C++ 异步编程中获取线程执行结果的方法与技巧及其在前端开发中的应用探讨
    本文探讨了C++异步编程中获取线程执行结果的方法与技巧,并深入分析了这些技术在前端开发中的应用。通过对比不同的异步编程模型,本文详细介绍了如何高效地处理多线程任务,确保程序的稳定性和性能。同时,文章还结合实际案例,展示了这些方法在前端异步编程中的具体实现和优化策略。 ... [详细]
  • 在PHP中实现腾讯云接口签名,以完成人脸核身功能的对接与签名配置时,需要注意将文档中的POST请求改为GET请求。具体步骤包括:使用你的`secretKey`生成签名字符串`$srcStr`,格式为`GET faceid.tencentcloudapi.com?`,确保参数正确拼接,避免因请求方法错误导致的签名问题。此外,还需关注API的其他参数要求,确保请求的完整性和安全性。 ... [详细]
  • IOS Run loop详解
    为什么80%的码农都做不了架构师?转自http:blog.csdn.netztp800201articledetails9240913感谢作者分享Objecti ... [详细]
  • 精选在线API编辑工具推荐
    本文将介绍几款优秀的在线API编辑工具,包括它们的功能特点和使用方法,帮助开发者提高工作效率。 ... [详细]
  • 开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤
    开发技巧:在Interface Builder中实现UIButton文本居中对齐的方法与步骤 ... [详细]
  • 技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统
    技术分享:使用 Flask、AngularJS 和 Jinja2 构建高效前后端交互系统 ... [详细]
  • 本文详细解析了使用C++实现的键盘输入记录程序的源代码,该程序在Windows应用程序开发中具有很高的实用价值。键盘记录功能不仅在远程控制软件中广泛应用,还为开发者提供了强大的调试和监控工具。通过具体实例,本文深入探讨了C++键盘记录程序的设计与实现,适合需要相关技术的开发者参考。 ... [详细]
  • 在《Linux高性能服务器编程》一书中,第3.2节深入探讨了TCP报头的结构与功能。TCP报头是每个TCP数据段中不可或缺的部分,它不仅包含了源端口和目的端口的信息,还负责管理TCP连接的状态和控制。本节内容详尽地解析了TCP报头的各项字段及其作用,为读者提供了深入理解TCP协议的基础。 ... [详细]
  • 服务器部署中的安全策略实践与优化
    服务器部署中的安全策略实践与优化 ... [详细]
  • 深入解析Android 4.4中的Fence机制及其应用
    在Android 4.4中,Fence机制是处理缓冲区交换和同步问题的关键技术。该机制广泛应用于生产者-消费者模式中,确保了不同组件之间高效、安全的数据传输。通过深入解析Fence机制的工作原理和应用场景,本文探讨了其在系统性能优化和资源管理中的重要作用。 ... [详细]
  • V8不仅是一款著名的八缸发动机,广泛应用于道奇Charger、宾利Continental GT和BossHoss摩托车中。自2008年以来,作为Chromium项目的一部分,V8 JavaScript引擎在性能优化和技术创新方面取得了显著进展。该引擎通过先进的编译技术和高效的垃圾回收机制,显著提升了JavaScript的执行效率,为现代Web应用提供了强大的支持。持续的优化和创新使得V8在处理复杂计算和大规模数据时表现更加出色,成为众多开发者和企业的首选。 ... [详细]
  • 深入探索HTTP协议的学习与实践
    在初次访问某个网站时,由于本地没有缓存,服务器会返回一个200状态码的响应,并在响应头中设置Etag和Last-Modified等缓存控制字段。这些字段用于后续请求时验证资源是否已更新,从而提高页面加载速度和减少带宽消耗。本文将深入探讨HTTP缓存机制及其在实际应用中的优化策略,帮助读者更好地理解和运用HTTP协议。 ... [详细]
  • 利用爬虫技术抓取数据,结合Fiddler与Postman在Chrome中的应用优化提交流程
    本文探讨了如何利用爬虫技术抓取目标网站的数据,并结合Fiddler和Postman工具在Chrome浏览器中的应用,优化数据提交流程。通过详细的抓包分析和模拟提交,有效提升了数据抓取的效率和准确性。此外,文章还介绍了如何使用这些工具进行调试和优化,为开发者提供了实用的操作指南。 ... [详细]
author-avatar
聂依依mma
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有